<?php
/**
 * 微信公众号支付
 * 作者：MYL
 */
namespace app\api\controller;
header("Content-type: text/html; charset=utf-8");

class Wxpay extends Base
{
    /**
     * 定义配置项
     * @var array
     */
    protected $config = array(
        'APPID' => 'wxaddeada93a401da5', // 微信支付APPID
        'MCHID' => '1483786642', // 微信支付MCHID 商户收款账号
        'KEY' => 'H8E8tPAvOjfMTGwOMbdwoCphkH4IrYYG', // 微信支付KEY
        'APPSECRET' => '3059baeadf67953bbc93c62c0a42ff3b',  //公众帐号secert
    );

    /**
     * 统一下单
     * @param $out_trade_no
     * @param $total_fee
     * @return array
     */
    public function getPrePayOrder($out_trade_no,$openid,$total_fee,$notify_url){
        $data['appid'] = $this->config['APPID'];
        $data['body'] = '邦伲德（上海）网络技术有限公司';
        $data['mch_id'] = $this->config['MCHID'];
        $data['nonce_str'] = $this->getRandChar(32); //随机字符串
        $data['notify_url'] = $notify_url; //回调地址
        $data['openid'] = $openid;
        $data['out_trade_no'] = $out_trade_no;
        $data['spbill_create_ip'] = $this->get_client_ip(); //获取设备的IP
        $data['total_fee'] = $total_fee; //价格(分)
        $data['trade_type'] = 'JSAPI';//交易类型(JSAPI)
        $s = $this->makeSign($data);
        $data["sign"] = $s;
        $xml = $this->arrayToXml($data);//数组转xml
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址
        $response = $this->postXmlCurl($xml, $url);//post https请求 xml格式
        $result = $this->xmlToArray($response); //将微信返回的结果xml转成数组
        $row = $this->makeSignsTwo($result['prepay_id'],$result['nonce_str']); //第二次获取签名（返给前台）
        return $row;
    }

    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function makeSign($data){
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->ToUrlParams($data);
        $config = $this->config;
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $config['KEY'];
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 执行第二次签名，才能返回给客户端使用
     */
    public function makeSignsTwo($prepayId,$nonce_str){
        $time = time();
        $map["appId"] = $this->config["APPID"];
        $map["package"] = 'prepay_id='.$prepayId;
        $map["nonceStr"] = $nonce_str;
        $map["timeStamp"] = strval($time);
        $map["signType"] = 'MD5';
        $s = $this->makeSign($map);
        $map["paySign"] = $s;
        return $map;
    }

    /**
     * 格式化参数格式化成url参数
     */
    public function ToUrlParams($data){
        $buff = "";
        foreach ($data as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 数组转XML
     * @param $arr
     * @return string
     */
    public function arrayToXml($arr){
        $xml = "<xml>";
        foreach ($arr as $key=>$val) {
            if (is_numeric($val)) {
                $xml.="<".$key.">".$val."</".$key.">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * XML转数组
     * @param $xml
     * @return mixed
     */
    public function xmlToArray($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }

    /**
     * 微信登陆开始
     */
    public function start(){
        $config = $this->config;
        $redirect_uri = "http://vip.bndvip.com/api/Pay/wxPay";
        $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $config['APPID'] . '&redirect_uri=' . urlencode($redirect_uri) . '&response_type=code&scope=snsapi_userinfo&state=state#wechat_redirect';
        header('location:' . $url);
        exit();
    }

    /**
     * 获取授权token
     * @param $code
     * @return bool|string
     */
    public function getUserAccessToken($code){
        $config = $this->config;
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$config['APPID']."&secret=".$config['APPSECRET']."&code=$code&grant_type=authorization_code";
        $res = file_get_contents($url);
        return json_decode($res);
    }

    /**
     * 获取用户信息
     * @param $accessToken
     * @return mixed
     */
    public function getUserInfo($accessToken){
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token=$accessToken->access_token&openid=$accessToken->openid&lang=zh_CN";
        $UserInfo = file_get_contents($url);
        return json_decode($UserInfo, true);
    }

    /**
     * 获取指定长度的随机字符串
     * @param $length
     * @return null|string
     */
    public function getRandChar($length){
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol)-1;
        for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }

        return $str;
    }

    /**
     * post https请求，CURLOPT_POSTFIELDS xml格式
     * @param $xml
     * @param $url
     * @param int $second
     * @return bool|mixed
     */
    public function postXmlCurl($xml,$url,$second=30){
        //初始化curl
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error"."<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }

    /**
     * 获取当前服务器的IP
     * @return array|false|string
     */
    public function get_client_ip(){
        if ($_SERVER['REMOTE_ADDR']) {
            $cip = $_SERVER['REMOTE_ADDR'];
        } elseif (getenv("REMOTE_ADDR")) {
            $cip = getenv("REMOTE_ADDR");
        } elseif (getenv("HTTP_CLIENT_IP")) {
            $cip = getenv("HTTP_CLIENT_IP");
        } else {
            $cip = "unknown";
        }
        return $cip;
    }

    /**
     *  验证 回调验证
     * @return bool
     */
    public function notify(){
        // 获取xml
        $xml = file_get_contents('php://input', 'r');

        // 转成php数组
        $data = $this->xmlToArray($xml);

        // 保存原sign
        $data_sign = $data['sign'];
        // sign不参与签名
        unset($data['sign']);
        $sign = $this->makeSign($data);
        // 判断签名是否正确  判断支付状态
        if ($sign === $data_sign && $data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS') {
            $result = $data;
        } else {
            $result = false;
        }
        // 返回状态给微信服务器
        if ($result) {
            $str = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        } else {
            $str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
        }
        echo $str;
        return $result;
    }

    /**
     * 微信回调数据处理
     */
    public function callbackNotify(){
        $result = $this->notify();
        if ($result) {
//            $myfile = fopen("小麻花辫.txt", "a+");
//            fwrite($myfile,json_encode($result,JSON_UNESCAPED_UNICODE));
//            fclose($myfile);
            /** 业务逻辑流程 **/
            /**1*///修改订单（订单状态、支付时间、微信支付订单号）
            $data['pay_status'] = 1;
            $data['pay_time'] = strtotime($result['time_end']);
            $data['transaction_id'] = $result['transaction_id']; //微信支付订单号
            //修改订单（支付转态，支付时间，微信支付订单号）
            M("order")->where(["order_sn"=>$result['out_trade_no']])->update($data);
            /**2*///修改商品表|查询当前订单商品的-->所有商品（商品ID，商品数量，商品规格ID）
            $order = M("order")->field('order_id,pay_code,pay_name,coupon_price,user_id')->where(["order_sn"=>$result['out_trade_no']])->find(); //订单表
            $order_goods = M("order_goods")->field("goods_id,goods_num,spec_key,spec_key_name")->where(["order_id"=>$order['order_id']])->find(); //订单商品表
            //根据订单商品的商品ID，查询出商品表的（商品销量、库存数量）
            $goods_num =  M("goods")->field('sales_sum,store_count')->where("goods_id",$order_goods['goods_id'])->find();
            $sales_sum = $goods_num['sales_sum'] + $order_goods['goods_num'];//商品销量
            $kucun =  $goods_num['store_count'] - $order_goods['goods_num'];// 商品库存
            //修改商品表（商品销量，商品库存）
            M('goods')->where("goods_id",$order_goods['goods_id'])->update(["sales_sum"=>$sales_sum,'store_count'=>$kucun]);
            /**3*///修改商品规格表|查询出对应规格的库存
            $spec = M("spec_goods_price")->field("store_count")->where(['goods_id'=>$order_goods['goods_id'],'key'=>$order_goods['spec_key']])->find();
            $spec_num = $spec['store_count'] - $order_goods['goods_num'];
            //修改商品规格表（规格库存数量）
            M("spec_goods_price")->where(['goods_id'=>$order_goods['goods_id'],'key'=>$order_goods['spec_key']])->update(['store_count'=>$spec_num]);
            /**4*///优惠券处理（修改优惠券使用状态）
            if ($order['coupon_price'] != 0) {
                M("coupon_exchange")->where(['user_id'=>$order['user_id'], 'status'=>0, 'money'=>$order['coupon_price']])->update(['status'=>1]);
            }
            /**5*///订单表-->到期时间
            if ($order_goods['spec_key_name'] == '年份:一年') {
                $oneYear = strtotime('+1 year',time());
                $map['expire_date'] = $oneYear;
            } if ($order_goods['spec_key_name'] == '年份:三年') {
                $oneYear = strtotime('+3 year',time());
                $map['expire_date'] = $oneYear;
            } if ($order_goods['spec_key_name'] == '年份:五年') {
                $oneYear = strtotime('+5 year',time());
                $map['expire_date'] = $oneYear;
            }
            M("order")->where(["order_sn"=>$result['out_trade_no']])->update($map);
            /**6*///添加支付记录
            $pay['pay_desc'] = '订单支付';
            $pay['pay_order'] = $result['transaction_id'];
            $pay['is_online'] = 1; //是否在线支付
            $pay['pay_code'] = $order['pay_code'];
            $pay['pay_name'] = $order['pay_name'];
            M("payment")->insert($pay); //添加支付记录
        } else {
            error_log("支付失败！", 3, 'err_log.txt');
        }
    }

    /**
     * 余额充值回调
     */
    public function rechargeNotify()
    {
        $result = $this->notify();
        if ($result) {
//            $myfile = fopen("小麻花辫3.txt", "a+");
//            fwrite($myfile,json_encode($result,JSON_UNESCAPED_UNICODE));
//            fclose($myfile);
            /**1*///修改订单（订单状态、支付时间、微信支付订单号）
            $data['pay_status'] = 1;
            $data['pay_code'] = '微信充值余额';
            $data['pay_time'] = strtotime($result['time_end']);
            $data['account'] = $result['cash_fee']/100; //微信支付订单号
            //修改订单（支付状态，支付时间，微信支付订单号）
            M("recharge")->where(["order_sn"=>$result['out_trade_no']])->update($data);
            //修改账户余额
            $recharge =  M("recharge")->field('order_id,order_sn,user_id,account')->where(["order_sn"=>$result['out_trade_no']])->find();
            $user_money = M("users")->field('user_money')->where('user_id',$recharge['user_id'])->find();
            $userMoney = $user_money['user_money'] + $recharge['account'];
            M("users")->where('user_id',$recharge['user_id'])->update(['user_money'=>$userMoney]);
            //新增用户资金变动表
            $map['user_id'] = $recharge['user_id'];
            $map['user_money'] = $result['cash_fee'];
            $map['change_time'] = strtotime($result['time_end']);
            $map['desc'] = '充值余额';
            $map['order_sn'] = $recharge['order_sn'];
            $map['order_id'] = $recharge['order_id'];
            M('account_log')->insert($map);
        }
    }

}